#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> 
#include <unistd.h>
#include <linux/input.h>
#include <linux/time.h>
#include <sys/timeb.h>
#include <poll.h>

/* Android munges Linux headers to avoid copyright issues, but doesn't munge linux/uinput.h,
 * so constants reproduced here.
 */
#define UI_SET_EVBIT   0x40045564
#define UI_SET_KEYBIT  0x40045565
#define UI_SET_RELBIT  0x40045566
#define UI_SET_ABSBIT  0x40045567

const char *EV_PREFIX = "/dev/input/";
char *filename;

/* NB event4 is the compass -- not required for tests. */
char *ev_devices[] = { "event0", "event1", "event2", "event3"/*, "event4" */}; //原始代码是注释event4的，有些手机没有event4
#define NUM_DEVICES (sizeof(ev_devices) / sizeof(char *))

int out_fds[NUM_DEVICES];
int num_events;
int in_fd;
int out_fd;

/* const char *OUT_PREFIX = "/sdcard/"; */

struct pollfd in_fds[NUM_DEVICES];
/*
 int out_fds[NUM_DEVICES];
 */

int initReplay() {
	char buf[256];
	int i;
	struct stat statinfo;

	for (i = 0; i < NUM_DEVICES; i++) {
		sprintf(buf, "%s%s", EV_PREFIX, ev_devices[i]);
		out_fds[i] = open(buf, O_WRONLY | O_NDELAY);
		if (out_fds[i] < 0) {
			printf("Couldn't open output device\n");
			return 1;
		}
	}

	if (stat(filename, &statinfo) == -1) {
		printf("Couldn't stat input\n");
		return 2;
	}

	num_events = statinfo.st_size / (sizeof(struct input_event) + sizeof(int));

	if ((in_fd = open(filename, O_RDONLY)) < 0) {
		printf("Couldn't open input\n");
		return 3;
	}

	// Hacky ioctl init
	ioctl(out_fds[3], UI_SET_EVBIT, EV_KEY);
	ioctl(out_fds[3], UI_SET_EVBIT, EV_REP);
	ioctl(out_fds[1], UI_SET_EVBIT, EV_ABS);

	return 0;
}

int replay() {
	struct timeval tdiff;
	struct input_event event;
	int i, outputdev;

	timerclear(&tdiff);

	for (i = 0; i < num_events; i++) {
		struct timeval now, tevent, tsleep;

		if (read(in_fd, &outputdev, sizeof(outputdev)) != sizeof(outputdev)) {
			printf("Input read error\n");
			return 1;
		}

		if (read(in_fd, &event, sizeof(event)) != sizeof(event)) {
			printf("Input read error\n");
			return 2;
		}

		gettimeofday(&now, NULL);
		if (!timerisset(&tdiff)) {
			timersub(&now, &event.time, &tdiff);
		}

		timeradd(&event.time, &tdiff, &tevent);
		timersub(&tevent, &now, &tsleep);
		if (tsleep.tv_sec > 0 || tsleep.tv_usec > 100)
			select(0, NULL, NULL, NULL, &tsleep);

		event.time = tevent;

		if (write(out_fds[outputdev], &event, sizeof(event)) != sizeof(event)) {
			printf("Output write error\n");
			return 3;
		}

//		printf("input %d, time %ld.%06ld, type %d, code %d, value %d\n", outputdev,
//				event.time.tv_sec, event.time.tv_usec, event.type, event.code, event.value);
	}
	printf("replay complete!\n");
	return 0;
}

long long getSystemTime() {
	struct timeb t;
	ftime(&t);
	return 1000 * t.time + t.millitm;
}

int initRecord() {
	char buffer[256];
	int i, fd;
	out_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
	if (out_fd < 0) {
		printf("Couldn't open output file\n");
		return 1;
	}

	for (i = 0; i < NUM_DEVICES; i++) {
		sprintf(buffer, "%s%s", EV_PREFIX, ev_devices[i]);
		in_fds[i].events = POLLIN;
		in_fds[i].fd = open(buffer, O_RDONLY | O_NDELAY);
		if (in_fds[i].fd < 0) {
			printf("Couldn't open input device %s\n", ev_devices[i]);
			return 2;
		}

#if 0
		sprintf(buffer, "%s%s", OUT_PREFIX, ev_devices[i]);
		out_fds[i] = open(buffer, O_WRONLY | O_CREAT);
		if(out_fds[i] < 0) {
			printf("Couldn't open output file %s\n", ev_devices[i]);
			return 2;
		}
#endif
	}
	return 0;
}
//（注意！时长控制有可能并不准确！因为下面有阻塞方法）
int record(int time) {
	int i, numread;
	struct input_event event;
	long long endTime = time * 1000 + getSystemTime();

	while (1) {

		if (poll(in_fds, NUM_DEVICES, -1) < 0) {
			printf("Poll error\n");
			return 1;
		}

		for (i = 0; i < NUM_DEVICES; i++) {
			if (in_fds[i].revents & POLLIN) {
				/* Data available */
				numread = read(in_fds[i].fd, &event, sizeof(event));
				if (numread != sizeof(event)) {
					printf("Read error\n");
					return 2;
				}
				if (getSystemTime() < endTime) { //时长控制

					if (write(out_fd, &i, sizeof(i)) != sizeof(i)) {
						printf("Write error\n");
						return 3;
					}
					if (write(out_fd, &event, sizeof(event)) != sizeof(event)) {
						printf("Write error\n");
						return 4;
					}

//				printf("input %d, time %ld.%06ld, type %d, code %d, value %d\n", i,
//						event.time.tv_sec, event.time.tv_usec, event.type, event.code, event.value);
				} else {
					goto END;
				}
			}
		}
	}
	END: {
		printf("record complete!\n");
		return 0;
	}
}
/**
 * 文件最好保存到sdcard
 *
 * 录制 60秒
 * wali /sdcard/1.txt 60
 *
 * 播放
 * wali /sdcard/1.txt
 */
int main(int argc, char *argv[]) {
	if (argc < 2 || argc > 3) {
		printf("usage:\n\trecord: wali file time\n\treplay: wali file\n\n");
		printf("\t\t\tmrpej.com (C) 2010-2014\n\n");
		return 0;
	}
	int ret;
	filename = argv[1];
	if (argc == 2) { //播放
		printf("replay: %s\n", filename);
		ret = initReplay();
		if (ret == 0) {
			return replay();
		}
	} else if (argc == 3) { //录制
		int time = atoi(argv[2]);
		printf("record:\n\ttime:%ds\n\ttoFile:%s\n\n", time, filename);
		if (time <= 0) {
			printf("error! (time <= 0)\n");
			return -1;
		}
		ret = initRecord();
		if (ret == 0) {
			return record(time);
		}
	}
	return ret;
}

